﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace MyUtils
{
	// todo 2013.01.01 - remove this class
	// todo 2012.06.01 - make obsolete with error
	// 2013.06.29 - I finally think that this is a good class. You can store a reference to any strongly typed object by any object. This should not force you to declare cache instance somewhere, this is truly system-wide.
	// todo use WeakValueCache internally to remove duplication
	[System.ComponentModel.Browsable(false)]
	[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
	// [Obsolete("Consider WeakValueCache<THandlerClass> instead.")]
	public static class Cache<T> where T : class
	{
		public class CacheIndexer
		{
			internal CacheIndexer()
			{
			}
//            public CacheIndexer()
//            {
//                var th = new Thread(delegate()
//                {
//                    while (true)
//                    {
//                        Thread.Sleep(1000);
//                        int cnt;
//                        object[] keysToRemove;
//                        lock (_dicSyncRoot)
//                        {
//                            keysToRemove = _dic.Where(kvp => !kvp.Value.IsAlive).Select(x => x.Key).ToArray();
//                            for (int i = 0; i < keysToRemove.Length; i++)
//                            {
//                                _dic.Remove(keysToRemove[i]);
//                            }
//                            cnt = _dic.Count;
//                        }
//#if DEBUG
//                        _totalPurged += keysToRemove.Length;
//#endif
//                        Debug.WriteLine(string.Format("Cache<{0}> items = {1}, \t purged = {2}", typeof(TTarget).Name, cnt, _totalPurged));
//                    }
//                });
//                th.Name = "_ Cache Cleanuper";
//                th.IsBackground = true;
//                th.Priority = ThreadPriority.Lowest;
//                th.Start();
//            }

			public T this[object key]
			{
				get
				{
					return GetOrCreate(key, null);
				}
			}

			public T this[object key, Func<T> factory]
			{
				get
				{
					return GetOrCreate(key, factory);
				}
			}
		}

		public static CacheIndexer Get
		{
			get { return _get; }
		}

// ReSharper disable StaticFieldInGenericType
		static readonly CacheIndexer _get = new CacheIndexer();
		static readonly Dictionary<object, WeakReference> _dic = new Dictionary<object, WeakReference>();
		static readonly object _dicSyncRoot = new object();

		static byte _seed;
// ReSharper restore StaticFieldInGenericType

#if DEBUG
		static long _totalPurged;
#endif

		static T GetOrCreate(object key, Func<T> factory)
		{
			if (key == null) return factory();
			var item = default(T);
			lock (_dicSyncRoot)
			{
				WeakReference wr;
				var inDic = _dic.TryGetValue(key, out wr);
				if (inDic)
				{
					item = (T)wr.Target;
				}

				if (item == null && factory != null)
				{
					// create
					item = factory();

					if (inDic)
					{
						wr.Target = item;
					}
					else
					{
						_dic.Add(key, new WeakReference(item));
					}
				}
			}

			// collect garbage
			if (unchecked(++_seed) == 0)
			{
				var toRemove = _dic.Where(kvp => !kvp.Value.IsAlive).Select(x => x.Key).ToArray();
				foreach (object t in toRemove)
				{
					_dic.Remove(t);
				}
#if DEBUG
				_totalPurged += toRemove.Length;
				Debug.WriteLine(string.Format("Cache<{0}> items = {1}, \t purged = {2}", typeof(T).Name, _dic.Count, _totalPurged));
#endif
			}

			return item;
		}
	}
}
